/**
 * @Description: 人物坐标向量计算
 * @author Beon
 * @date 2022/4/19
*/
import * as THREE from 'three';
import gsap from 'gsap';
import { Vector3 } from 'three/src/math/Vector3';
import { subscribe } from '../../../../utils/tools/tools';

export type MoveState = 0 | 1 | -1;

class Direction {
    static direction: Direction;

    Vector3Camera: THREE.Vector3 = new THREE.Vector3();
    moveSpeed = 0;

    private readonly Vector3Direction: THREE.Vector3;
    private moveState = {
      gsap: null as GSAPTween | null,
      gsap1: null as GSAPTween | null,
      x: 0 as MoveState,
      mulX: 0,
      z: 0 as MoveState,
      mulZ: 0
    };
    private maxSpeed = 0.2;
    private stepSpeed = 0.0035;

    constructor() {
      this.Vector3Direction = new THREE.Vector3();
      this.createAnimation();
    }

    open(type: string) {
      switch (type) {
        case 'KeyW':
          this.moveState.z = 1;
          break;
        case 'KeyS':
          this.moveState.z = -1;
          break;
        case 'KeyA':
          this.moveState.x = -1;
          break;
        case 'KeyD':
          this.moveState.x = 1;
          break;
      }
      this.computeDirection();
    }

    close(type: string) {
      switch (type) {
        case 'KeyW':
          this.moveState.z = 0;
          break;
        case 'KeyS':
          this.moveState.z = 0;
          break;
        case 'KeyA':
          this.moveState.x = 0;
          break;
        case 'KeyD':
          this.moveState.x = 0;
          break;
      }
      this.computeDirection();
    }

    private computeDirection() {
      if (this.moveState.gsap) this.moveState.gsap.kill();
      if (this.moveState.gsap1) this.moveState.gsap1.kill();
      const dealVector3 = new THREE.Vector3().subVectors(
        this.Vector3Camera,
        this.Vector3Direction
      );

      const baseZ = this.computeVector(dealVector3, 'z');
      // 向量旋转
      const cacheX = dealVector3.x;
      dealVector3.setX(-dealVector3.z);
      dealVector3.setZ(cacheX);

      const baseX = this.computeVector(dealVector3, 'x');

      this.moveState.gsap = gsap.to(this.moveState, {
        duration: 0.3,
        mulX: this.moveState.x !== 0 && this.moveState.z !== 0
          ? (baseX.x + baseZ.x) / Math.pow(2, 1 / 2)
          : baseX.x + baseZ.x,
        mulZ: this.moveState.x !== 0 && this.moveState.z !== 0
          ? (baseX.z + baseZ.z) / Math.pow(2, 1 / 2)
          : baseX.z + baseZ.z
      });
    }

    private createAnimation() {
      const animate = () => {
        this.moveCheck();
        this.Vector3Direction.x += this.moveSpeed * this.moveState.mulX;
        this.Vector3Direction.z += this.moveSpeed * this.moveState.mulZ;
        subscribe.emit('directionAn', {
          vector3Direction: this.Vector3Direction,
          changeVector3: new Vector3(this.moveSpeed * this.moveState.mulX, 0, this.moveSpeed * this.moveState.mulZ)
        });
        requestAnimationFrame(animate);
      };
      animate();
    }

    private moveCheck() {
      if (this.moveState.z === 0 && this.moveState.x === 0) {
        if (this.moveSpeed >= this.stepSpeed / 2) {
          this.moveSpeed -= this.stepSpeed;
        } else {
          this.moveSpeed = 0;
          this.moveState.mulX = 0;
          this.moveState.mulZ = 0;
        }
      } else {
        this.moveSpeed =
                this.moveSpeed >= this.maxSpeed
                  ? this.maxSpeed
                  : this.moveSpeed + this.stepSpeed;
      }

    }

    private static computeWithSign(vector3: Vector3, type: 'x' | 'z') {
      return vector3[type] >= 0 ? -1 : 1;
    }

    private computeVector(dealVector3: Vector3, type: 'x' | 'z') {
      return {
        x: Math.pow(
          1 / (1 + Math.pow(dealVector3.z, 2) / Math.pow(dealVector3.x, 2)),
          1 / 2
        ) * this.moveState[type] * Direction.computeWithSign(dealVector3, 'x'),
        z: Math.pow(
          1 / (1 + Math.pow(dealVector3.x, 2) / Math.pow(dealVector3.z, 2)),
          1 / 2
        ) * this.moveState[type] * Direction.computeWithSign(dealVector3, 'z')
      };
    }
}
// 单例模式保证
Direction.direction = new Direction();

export default Direction.direction;
